Skip to content

streaming code used to hang when it was asked to "exit" - fixed now#635

Merged
dpebot merged 9 commits intomasterfrom
streaming_fix
Nov 8, 2016
Merged

streaming code used to hang when it was asked to "exit" - fixed now#635
dpebot merged 9 commits intomasterfrom
streaming_fix

Conversation

@puneith
Copy link
Copy Markdown
Contributor

@puneith puneith commented Nov 4, 2016

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Nov 4, 2016
@puneith puneith changed the title added streaming fix when exit streaming code used to hang when "exit" - fixed now Nov 4, 2016
@puneith puneith changed the title streaming code used to hang when "exit" - fixed now streaming code used to hang when it was asked to "exit" - fixed now Nov 4, 2016
Copy link
Copy Markdown
Contributor

@jerjou jerjou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were you able to reproduce this bug? (I assume this is to address #608) I wasn't able to reproduce, which is why I hadn't responded to the bug yet..


audio_stream.stop_stream()
audio_stream.close()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this move necessary? I'd prefer to have the function that opened the stream be responsible for closing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you. But, the thread hangs and doesn't come out of the while loop. So, I had to move here and close when I get out.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - this is because buff.put(None) is never executed if the above loop is exited because stoprequest.is_set() is True. (this means the blocking buff.get() in _audio_data_generator blocks forever, since it's never given the None to exit from its loop).

If you change the above loop to:

try:
    ...
except IOError:
    pass
finally:
    buff.put(None)

then you should be able to put this back.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this happens because the stream is stopped and the thread hangs when this thread is still in the loop.

audio_stream.stop_stream()
audio_stream.close()
fill_buffer_thread.join()
audio_interface.terminate()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason to remove this line? It seems like good hygiene..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That might have been a mistake. I will restore it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"""
while True:
# Use a blocking get() to ensure there's at least one chunk of data
while not stoprequest.isSet():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer .is_set, for python style conventions. Ditto below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done



def _audio_data_generator(buff):
def _audio_data_generator(buff, stoprequest):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_audio_data_generator shouldn't need this. Once _fill_buffer gets the signal, it'll add None to the buffer, which will cause this generator to exit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, fill_buffer doesn't add the None unless it receives IOError which it doesn't. Ok let me make one change to _fill_buffer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually to think of it making both threads stop from the same signal is better.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain your reasoning?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reasoning: Both reader and writer threads consider the stopsignal. If reader looks at the indirect signal from the writer, we don't know if it needed to stop because there is really a stop signal or an error in the writer's output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... sure, okay, I can see that. I think there's a case for just letting _fill_buffer propagate the way it is, but I won't block on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had thought about both cases, but can now also see the case for other option. And now as I am thinking more I think the other option makes more sense :) Let me go back to it.

@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 4, 2016

Yes I was able to reproduce this. It did hang on my mac.

while True:
# Use a blocking get() to ensure there's at least one chunk of data
while not stoprequest.is_set():
# Use a blocking get() to ensure there's at least one chunk of data
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove trailing whitespace



def _audio_data_generator(buff):
def _audio_data_generator(buff, stoprequest):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... sure, okay, I can see that. I think there's a case for just letting _fill_buffer propagate the way it is, but I won't block on it.


audio_stream.stop_stream()
audio_stream.close()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - this is because buff.put(None) is never executed if the above loop is exited because stoprequest.is_set() is True. (this means the blocking buff.get() in _audio_data_generator blocks forever, since it's never given the None to exit from its loop).

If you change the above loop to:

try:
    ...
except IOError:
    pass
finally:
    buff.put(None)

then you should be able to put this back.

"""
while True:
# Use a blocking get() to ensure there's at least one chunk of data
# Use a blocking get() to ensure there's at least one chunk of data
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


yield _audio_data_generator(buff)

audio_stream.stop_stream()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason not to stop the stream before closing? While I'm not certain it's necessary, all the examples stop the stream before closing it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the stream is stopped, one may not call write or read. And thread _fill_buffer gets stuck.

except IOError:
# This happens when the stream is closed. Signal that we're done.
buff.put(None)
pass
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this, you'll have a race condition. ie if _audio_data_generator's buff.get() is waiting for data in the buffer when stoprequest is triggered, it could potentially wait forever and never yield a value.

Copy link
Copy Markdown
Contributor

@jerjou jerjou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice - thanks for catching that race condition 😁
Looks good - just some polishing of the comments left.

# This happens when the stream is closed. Signal that we're done.
pass
finally:
buff.put(None)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment here describing what putting None means would be helpful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# First, a thread that collects audio data as it comes in
with record_audio(RATE, CHUNK) as buffered_audio_data:

# stop request
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this comment describe what stoprequest is for? Simply repeating the name of the variable isn't helpful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -208,7 +212,11 @@ def main():
make_channel('speech.googleapis.com', 443)) as service:
# For streaming audio from the microphone, there are three threads.
# First, a thread that collects audio data as it comes in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should be right above the with block, since that's what it's describing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

except queue.Empty:
break

# If the data contains None then set stop = True.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments should describe the meaning and purpose of the code, not just narrate what the code is doing. Maybe something like:

# If `_fill_buffer` adds `None` to the buffer, the audio stream is closed.
# Yield the final bit of the buffer and exit the loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# If the data contains None then set stop = True.
if None in data:
stop = True
data.remove(None)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah - nice catch.


def main():
# For streaming audio from the microphone, there are three threads.
# First, a thread that collects audio data as it comes in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - I meant that this comment should be on the below with statement, where it was before. ie since the comment is describing the thread that collects audio data, it should be just before the record_audio call.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

with record_audio(RATE, CHUNK) as buffered_audio_data:

# stoprequest is event object which is set in `listen_print_loop`.
# To indicate that the trancsription should be stopped.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trancsription -> transcription

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

with record_audio(RATE, CHUNK) as buffered_audio_data:

# stoprequest is event object which is set in `listen_print_loop`.
# To indicate that the trancsription should be stopped.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 219 and 220 are the same sentence (otherwise, line 220 is a sentence fragment). Please change to:

# stoprequest is an event object which is set in `listen_print_loop` to indicate that the transcription should be stopped.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


# stoprequest is event object which is set in `listen_print_loop`.
# To indicate that the trancsription should be stopped.
# `_fill_buffer` checks and stops collecting data from audio_stream.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a bunch of trouble parsing this sentence. Perhaps:

# The `_fill_buffer` thread checks this object, and closes the `audio_stream` once it's set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Copy Markdown
Contributor

@jerjou jerjou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@puneith puneith closed this Nov 7, 2016
@puneith puneith reopened this Nov 7, 2016
@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 7, 2016

@dpebot merge when green.

@dpebot
Copy link
Copy Markdown
Collaborator

dpebot commented Nov 7, 2016

Okay! I'll merge when all statuses are green.

@dpebot dpebot added the automerge Merge the pull request once unit tests and other checks pass. label Nov 7, 2016
@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 7, 2016

Does dpebot know he can also merge when someone approves the PR + green status?

@jerjou
Copy link
Copy Markdown
Contributor

jerjou commented Nov 7, 2016

I'm not sure what you're asking. Are you suggesting that we should merge PRs before the automated tests pass?

@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 7, 2016

I am asking can dpebot take over when a) someone has approved the PR b) green status. Both a and b are true.

@jerjou
Copy link
Copy Markdown
Contributor

jerjou commented Nov 7, 2016

I don't understand what you're saying. Are you stating that - currently - someone has approved this PR AND the PR's status is green? Or are you asking whether dpebot can submit the PR if a OR b are true? Or are you asking whether dpebot can submit the PR when a AND b are true?

@jerjou
Copy link
Copy Markdown
Contributor

jerjou commented Nov 7, 2016

If it helps - IIUC, dpebot just waits for travis to turn green before submitting. Currently travis is still waiting for test results, so dpebot is not merging the PR yet.

@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 7, 2016

I thought my question was simple :)

In other words I am asking. Do we have to ask dpebot explicitly "merge when everything is green"? Can dpebot not figure that out automatically and do the needful?

@jerjou
Copy link
Copy Markdown
Contributor

jerjou commented Nov 7, 2016

I think your question is simple, but you're not expressing the full question, or you're not giving enough context for the question, so it's hard to figure out what you're asking. Perhaps it'd be helpful to structure your questions into three steps:

  1. What the current situation is
  2. What you expect the situation to be
  3. The question

For example:

  1. We ask dpebot to merge when green
  2. dpebot should be able to do this without us asking
  3. Do we need to ask dpebot, when we want to merge?

It was ambiguous in your original question what you thought the current situation was, and what you expected the situation to be. For instance - what did you mean by 'also'? That implies that there's another situation wherein dpebot will merge, besides when it's approved + green; but you don't describe this other situation you allude to, so there's no way for me to know what you're contrasting.

Does that make sense?

To answer your question - dpebot listens for travis to complete, and only merges if it has status:success AND the automerge label on it. Telling dpebot to 'merge when green' simply adds the automerge label. We don't want dpebot to just merge all green PRs because eg the user might still be working on the PR (for example, addressing comments from the reviewer), even if the tests pass.

@puneith
Copy link
Copy Markdown
Contributor Author

puneith commented Nov 7, 2016

Wow! Travis is sloooow!

@jerjou
Copy link
Copy Markdown
Contributor

jerjou commented Nov 8, 2016

Yeah - it's usually one slow-running project in our org that backs everything else up :-/ We might consider changing to circleci, like some of the other teams are doing..

@dpebot dpebot merged commit 08b4da6 into master Nov 8, 2016
@dpebot dpebot deleted the streaming_fix branch November 8, 2016 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Merge the pull request once unit tests and other checks pass. cla: yes This human has signed the Contributor License Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants